﻿using System;
using EntityFrameworkDemo;
using System.Text;
using System.Linq;
using EFCTP5VSNH3.Model;
using FizzWare.NBuilder;
using System.Data.Entity;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Validation;

namespace EFModelTesting
{


    /// <summary>
    ///This is a test class for AdressTest and is intended
    ///to contain all AdressTest Unit Tests
    ///</summary>
    [TestClass()]
    public class DbTest
    {


        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes


        [ClassInitialize()]
        public static void MyClassInitialize(TestContext testContext)
        {

            //System.Data.Entity.Database.SetInitializer<Db>(new DropCreateDatabaseAlways <Db>());
            //DbDatabase.SetInitializer<Db>(new CreateDatabaseIfNotExists<Db>());
            //DbDatabase.SetInitializer<Db>(new DropCreateDatabaseIfModelChanges<Db>());

            Database.SetInitializer<Db>(new DbCustomInitializerStrategy<Db>());

            using (Db db = new Db())
            {
                Customer customer = Builder<Customer>.CreateNew()
                    .With(c => c.CompanyAddress = Builder<Adress>.CreateNew().Build())
                    .With(c => c.Projects = new List<Project>(1))
                    .Build();

                Developer dev = Builder<Developer>.CreateNew()
                    .With(d => d.Skills = Builder<Skill>.CreateListOfSize(3).Build())
                    .With(d => d.Projects = new List<Project>(1))
                    .Build();

                Project prj = Builder<Project>.CreateNew()
                    .With(p => p.Customer = customer)
                    .With(p => p.Developers = new List<Developer>(1))
                    .Build();

                customer.Projects.Add(prj);
                prj.Developers.Add(dev);
                dev.Projects.Add(prj);

                db.Customers.Add(customer);
                db.Developers.Add(dev);
                db.Projects.Add(prj);

                int rowsAffected = db.SaveChanges();
            }
        }

        #endregion

        [TestMethod()]
        public void GetObjectContextFromDbContextTest()
        {
            using (Db db = new Db())
            {
                Assert.IsNotNull(db.ObjContext);
            }
        }

        [TestMethod()]
        public void GetCustomersCountTest()
        {
            using (Db db = new Db())
            {
                int customersInDb = (from c in db.Customers select c).Count();
                Assert.IsTrue(customersInDb > 0);
            }
        }

        [TestMethod()]
        public void FailAddProjectDeveloperTest()
        {
            using (Db db = new Db())
            {
                Project prj = Builder<Project>.CreateNew().Build();
                Developer dev = Builder<Developer>.CreateNew().Build();

                dev.Skills = Builder<Skill>.CreateListOfSize(3).Build();

                prj.Developers = new System.Collections.Generic.List<Developer>();
                dev.Projects = new System.Collections.Generic.List<Project>();

                prj.Developers.Add(dev);
                dev.Projects.Add(prj);

                Customer cust = (from c in db.Customers orderby c.Id select c).FirstOrDefault();

                Assert.IsNotNull(cust);

                prj.Customer = cust;
                cust.Projects.Add(prj);

                dev.Name = String.Empty;
                dev.Surname = String.Empty;

                prj.Description = String.Empty;

                StringBuilder sb = new StringBuilder();
                IEnumerable<DbEntityValidationResult> entitiesValidationResult = db.GetValidationErrors();

                if (entitiesValidationResult.Count() > 0)
                {
                    foreach (DbEntityValidationResult entityValidationResult in entitiesValidationResult)
                    {
                        sb.Append("Error in " + entityValidationResult.Entry.Entity);
                        sb.AppendLine();

                        foreach (DbValidationError validationError in entityValidationResult.ValidationErrors)
                        {
                            sb.AppendFormat("{0} : {1}", validationError.PropertyName, validationError.ErrorMessage);
                            sb.AppendLine();
                        }
                    }

                    Assert.IsTrue(entitiesValidationResult.Count() > 0, sb.ToString());
                }
                else
                {
                    int rowsAffected = db.SaveChanges();

                    Assert.Fail("Entities are not valid. Cannot be saved!!!");
                }
            }
        }

        [TestMethod()]
        public void ExecuteDeveloperSqlQueryTest()
        {
            using (Db db = new Db())
            {
                Developer developer = db.ExecuteDeveloperSqlQuery(@"SELECT S.Id as Id, S.Nome as Name, S.Cognome as Surname FROM Sviluppatore as S").FirstOrDefault();
                Assert.IsNotNull(developer);
                Assert.IsTrue(!string.IsNullOrEmpty(developer.Name));
                Assert.IsTrue(!string.IsNullOrEmpty(developer.Surname));
            }
        }

        [TestMethod()]
        public void ExecuteDevelopersStoredProcedureTest()
        {
            using (Db db = new Db())
            {

                //IQueryable<Developer> developerTest = from d in db.Developers.Include(d => d.Skills) select d;
                // List<Developer> a =  developerTest.ToList();

                IList<Developer> noTrackedDevelopers = db.NoTrackedGetDevelopersByName("n").ToList();

                Assert.IsNotNull(noTrackedDevelopers);
                Assert.IsTrue(noTrackedDevelopers.Count() > 0);

                Developer noTrackedDeveloper = noTrackedDevelopers.First();

                noTrackedDeveloper.Name = "Pietro";
                noTrackedDeveloper.Surname = "Libro";

                int rowsAffected = db.SaveChanges();

                Assert.IsTrue(rowsAffected == 0);

                IList<Developer> trackedDevelopers = db.TrackedGetDevelopersByName("n").ToList();

                Assert.IsNotNull(trackedDevelopers);
                Assert.IsTrue(trackedDevelopers.Count() > 0);

                Developer trackedDeveloper = trackedDevelopers.First();

                trackedDeveloper.Name = "Pietro";
                trackedDeveloper.Surname = "Libro";

                try
                {
                    rowsAffected = db.SaveChanges();
                }
                catch (DbEntityValidationException ex)
                {
                    System.Console.WriteLine(ex.Message);
                }
                

                Assert.IsTrue(rowsAffected >= 1);

            }
        }

        [TestMethod()]
        public void PropertiesTest2()
        {
            using (Db db = new Db())
            {
                Developer d = db.Developers.First();
                ////Force entity update 
                //db.Entry(d).State = System.Data.EntityState.Modified;

                ////Force update only of surname property.
                db.Entry(d).Property(de => de.Surname).IsModified = true;
                db.SaveChanges();
            }
        }

        [TestMethod()]
        public void PropertiesTest()
        {
            DeveloperDTO developerDTO = new DeveloperDTO()
            {
                Name = "Pietro",
                Surname = "Libro"
            };

            using (Db db = new Db())
            {
                Developer developerStored = (from d in db.Developers select d).FirstOrDefault();

                if (developerStored != null)
                {
                    ////Original values.
                    DbPropertyValues originalValues = db.Entry(developerStored).OriginalValues;
                    Assert.IsTrue(originalValues["Name"].ToString().Equals("Name1"));
                    Assert.IsTrue(originalValues["Surname"].ToString().Equals("Surname1"));

                    ////Modified values.
                    db.Entry(developerStored).CurrentValues.SetValues(developerDTO);
                    DbPropertyValues currentValues = db.Entry(developerStored).CurrentValues;
                    Assert.IsTrue(currentValues["Name"].ToString().Equals("Pietro"));
                    Assert.IsTrue(currentValues["Surname"].ToString().Equals("Libro"));

                    //////Database values.
                    //DbPropertyValues databaseValues = db.Entry(developerStored).GetDatabaseValues();
                    //Assert.IsTrue(databaseValues["Name"].ToString().Equals("Name1"));
                    //Assert.IsTrue(databaseValues["Surname"].ToString().Equals("Surname1"));
                }
            }
        }

        [TestMethod()]
        public void ConcurrencyStoreWins()
        {
            using (Db db = new Db())
            {
                Developer developer = (from d in db.Developers select d).FirstOrDefault();

                Assert.IsNotNull(developer);

                ////Edit surname value.
                developer.Surname = "Cognome";

                ////Somebody edit same record...

                try
                {
                    db.SaveChanges();
                }
                catch (DbUpdateConcurrencyException ex)
                {
                    ex.Entries.Single().Reload();

                    db.SaveChanges();

                    Assert.AreEqual(developer.Surname, "Libro");
                }
            }
        }

        [TestMethod()]
        public void ConcurrencyClientWins()
        {
            using (Db db = new Db())
            {
                Developer developer = (from d in db.Developers select d).FirstOrDefault();

                Assert.IsNotNull(developer);

                ////Edit surname value.
                developer.Surname = "Cognome";

                ////Somebody edit same record...

                try
                {
                    db.SaveChanges();
                }
                catch (DbUpdateConcurrencyException ex)
                {
                    DbEntityEntry customer = ex.Entries.Single();
                    customer.OriginalValues.SetValues(customer.GetDatabaseValues());

                    db.SaveChanges();

                    Assert.AreEqual(developer.Surname, "Cognome");
                }
            }
        }
    }
}
